home *** CD-ROM | disk | FTP | other *** search
/ HyperLib 1997 Winter - Disc 1 / HYPERLIB-1997-Winter-CD1.ISO.7z / HYPERLIB-1997-Winter-CD1.ISO / オンラインウェア / PRG / PL 2.0 SupplementDoc Folder.sit / PL 2.0 SupplementDoc Folder / Documentation / Appendix C. Backquote < prev    next >
Lisp/Scheme  |  1995-03-23  |  20KB  |  520 lines

  1.  
  2. Appendix C.
  3. Backquote
  4.  
  5. [change_begin]
  6. Here is the code for an implementation of backquote syntax (see section 22.1.3)
  7. that I have found quite useful in explaining to myself the behavior of nested
  8. backquotes. It implements the formal rules for backquote processing and
  9. optionally applies a code simplifier to the result. One must be very careful in
  10. choosing the simplification rules; the rules given here work, but some Common
  11. Lisp implementations have run into trouble at one time or another by using a
  12. simplification rule that does not work in all cases. Code transformations that
  13. are plausible when single forms are involved are likely to fail in the presence
  14. of splicing.
  15.  
  16. At the end of this appendix are some samples of nested backquote syntax with
  17. commentary.
  18.  
  19. ;;; Common Lisp backquote implementation, written in Common Lisp.
  20. ;;; Author: Guy L. Steele Jr.     Date: 27 December 1985
  21. ;;; Tested under Symbolics Common Lisp and Lucid Common Lisp.
  22. ;;; This software is in the public domain.
  23.  
  24. ;;; $ is pseudo-backquote and % is pseudo-comma.  This makes it
  25. ;;; possible to test this code without interfering with normal
  26. ;;; Common Lisp syntax.
  27.  
  28. ;;; The following are unique tokens used during processing.
  29. ;;; They need not be symbols; they need not even be atoms.
  30.  
  31. (defvar *comma* (make-symbol "COMMA"))
  32. (defvar *comma-atsign* (make-symbol "COMMA-ATSIGN"))
  33. (defvar *comma-dot* (make-symbol "COMMA-DOT"))
  34. (defvar *bq-list* (make-symbol "BQ-LIST"))
  35. (defvar *bq-append* (make-symbol "BQ-APPEND"))
  36. (defvar *bq-list** (make-symbol "BQ-LIST*"))
  37. (defvar *bq-nconc* (make-symbol "BQ-NCONC"))
  38. (defvar *bq-clobberable* (make-symbol "BQ-CLOBBERABLE"))
  39. (defvar *bq-quote* (make-symbol "BQ-QUOTE"))
  40. (defvar *bq-quote-nil* (list *bq-quote* nil))
  41.  
  42. ;;; Reader macro characters:
  43. ;;;    $foo is read in as (BACKQUOTE foo)
  44. ;;;    %foo is read in as (#:COMMA foo)
  45. ;;;    %@foo is read in as (#:COMMA-ATSIGN foo)
  46. ;;;    %.foo is read in as (#:COMMA-DOT foo)
  47. ;;; where #:COMMA is the value of the variable *COMMA*, etc.
  48.  
  49. ;;; BACKQUOTE is an ordinary macro (not a read-macro) that
  50. ;;; processes the expression foo, looking for occurrences of
  51. ;;; #:COMMA, #:COMMA-ATSIGN, and #:COMMA-DOT.  It constructs code
  52. ;;; in strict accordance with the rules on pages 349-350 of
  53. ;;; the first edition (pages 528-529 of this second edition).
  54. ;;; It then optionally applies a code simplifier.
  55.  
  56. (set-macro-character #¥$
  57.   #'(lambda (stream char)
  58.       (declare (ignore char))
  59.       (list 'backquote (read stream t nil t))))
  60.  
  61. (set-macro-character #¥%
  62.   #'(lambda (stream char)
  63.       (declare (ignore char))
  64.         (case (peek-char nil stream t nil t)
  65.           (#¥@ (read-char stream t nil t)
  66.                (list *comma-atsign* (read stream t nil t)))
  67.           (#¥. (read-char stream t nil t)
  68.                (list *comma-dot* (read stream t nil t)))
  69.           (otherwise (list *comma* (read stream t nil t))))))
  70.  
  71.  
  72. ;;; If the value of *BQ-SIMPLIFY* is non-NIL, then BACKQUOTE
  73. ;;; processing applies the code simplifier.  If the value is NIL,
  74. ;;; then the code resulting from BACKQUOTE is exactly that
  75. ;;; specified by the official rules.
  76.  
  77. (defparameter *bq-simplify* t)
  78.  
  79. (defmacro backquote (x)
  80.   (bq-completely-process x))
  81.  
  82. ;;; Backquote processing proceeds in three stages:
  83. ;;;
  84. ;;; (1) BQ-PROCESS applies the rules to remove occurrences of
  85. ;;; #:COMMA, #:COMMA-ATSIGN, and #:COMMA-DOT corresponding to
  86. ;;; this level of BACKQUOTE.  (It also causes embedded calls to
  87. ;;; BACKQUOTE to be expanded so that nesting is properly handled.)
  88. ;;; Code is produced that is expressed in terms of functions
  89. ;;; #:BQ-LIST, #:BQ-APPEND, and #:BQ-CLOBBERABLE.  This is done
  90. ;;; so that the simplifier will simplify only list construction
  91. ;;; functions actually generated by BACKQUOTE and will not involve
  92. ;;; any user code in the simplification.  #:BQ-LIST means LIST,
  93. ;;; #:BQ-APPEND means APPEND, and #:BQ-CLOBBERABLE means IDENTITY
  94. ;;; but indicates places where "%." was used and where NCONC may
  95. ;;; therefore be introduced by the simplifier for efficiency.
  96. ;;;
  97. ;;; (2) BQ-SIMPLIFY, if used, rewrites the code produced by
  98. ;;; BQ-PROCESS to produce equivalent but faster code.  The
  99. ;;; additional functions #:BQ-LIST* and #:BQ-NCONC may be
  100. ;;; introduced into the code.
  101. ;;;
  102. ;;; (3) BQ-REMOVE-TOKENS goes through the code and replaces
  103. ;;; #:BQ-LIST with LIST, #:BQ-APPEND with APPEND, and so on.
  104. ;;; #:BQ-CLOBBERABLE is simply eliminated (a call to it being
  105. ;;; replaced by its argument).  #:BQ-LIST* is replaced by either
  106. ;;; LIST* or CONS (the latter is used in the two-argument case,
  107. ;;; purely to make the resulting code a tad more readable).
  108.  
  109. (defun bq-completely-process (x)
  110.   (let ((raw-result (bq-process x)))
  111.     (bq-remove-tokens (if *bq-simplify*
  112.                           (bq-simplify raw-result)
  113.                           raw-result))))
  114.  
  115. (defun bq-process (x)
  116.   (cond ((atom x)
  117.          (list *bq-quote* x))
  118.         ((eq (car x) 'backquote)
  119.          (bq-process (bq-completely-process (cadr x))))
  120.         ((eq (car x) *comma*) (cadr x))
  121.         ((eq (car x) *comma-atsign*)
  122.          (error ",@~S after `" (cadr x)))
  123.         ((eq (car x) *comma-dot*)
  124.          (error ",.~S after `" (cadr x)))
  125.         (t (do ((p x (cdr p))
  126.                 (q '() (cons (bracket (car p)) q)))
  127.                ((atom p)
  128.                 (cons *bq-append*
  129.                       (nreconc q (list (list *bq-quote* p)))))
  130.              (when (eq (car p) *comma*)
  131.                (unless (null (cddr p)) (error "Malformed ,~S" p))
  132.                (return (cons *bq-append*
  133.                              (nreconc q (list (cadr p))))))
  134.              (when (eq (car p) *comma-atsign*)
  135.                (error "Dotted ,@~S" p))
  136.              (when (eq (car p) *comma-dot*)
  137.                (error "Dotted ,.~S" p))))))
  138.  
  139. ;;; This implements the bracket operator of the formal rules.
  140.  
  141. (defun bracket (x)
  142.   (cond ((atom x)
  143.          (list *bq-list* (bq-process x)))
  144.         ((eq (car x) *comma*)
  145.          (list *bq-list* (cadr x)))
  146.         ((eq (car x) *comma-atsign*)
  147.          (cadr x))
  148.         ((eq (car x) *comma-dot*)
  149.          (list *bq-clobberable* (cadr x)))
  150.         (t (list *bq-list* (bq-process x)))))
  151.  
  152. ;;; This auxiliary function is like MAPCAR but has two extra
  153. ;;; purposes: (1) it handles dotted lists; (2) it tries to make
  154. ;;; the result share with the argument x as much as possible.
  155.  
  156. (defun maptree (fn x)
  157.   (if (atom x)
  158.       (funcall fn x)
  159.       (let ((a (funcall fn (car x)))
  160.             (d (maptree fn (cdr x))))
  161.         (if (and (eql a (car x)) (eql d (cdr x)))
  162.             x
  163.             (cons a d)))))
  164.  
  165. ;;; This predicate is true of a form that when read looked
  166. ;;; like %@foo or %.foo.
  167.  
  168. (defun bq-splicing-frob (x)
  169.   (and (consp x)
  170.        (or (eq (car x) *comma-atsign*)
  171.            (eq (car x) *comma-dot*))))
  172.  
  173.  
  174. ;;; This predicate is true of a form that when read
  175. ;;; looked like %@foo or %.foo or just plain %foo.
  176.  
  177. (defun bq-frob (x)
  178.   (and (consp x)
  179.        (or (eq (car x) *comma*)
  180.            (eq (car x) *comma-atsign*)
  181.            (eq (car x) *comma-dot*))))
  182.  
  183. ;;; The simplifier essentially looks for calls to #:BQ-APPEND and
  184. ;;; tries to simplify them.  The arguments to #:BQ-APPEND are
  185. ;;; processed from right to left, building up a replacement form.
  186. ;;; At each step a number of special cases are handled that,
  187. ;;; loosely speaking, look like this:
  188. ;;;
  189. ;;;  (APPEND (LIST a b c) foo) => (LIST* a b c foo)
  190. ;;;       provided a, b, c are not splicing frobs
  191. ;;;  (APPEND (LIST* a b c) foo) => (LIST* a b (APPEND c foo))
  192. ;;;       provided a, b, c are not splicing frobs
  193. ;;;  (APPEND (QUOTE (x)) foo) => (LIST* (QUOTE x) foo)
  194. ;;;  (APPEND (CLOBBERABLE x) foo) => (NCONC x foo)
  195.  
  196. (defun bq-simplify (x)
  197.   (if (atom x)
  198.       x
  199.       (let ((x (if (eq (car x) *bq-quote*)
  200.                    x
  201.                    (maptree #'bq-simplify x))))
  202.         (if (not (eq (car x) *bq-append*))
  203.             x
  204.             (bq-simplify-args x)))))
  205.  
  206. (defun bq-simplify-args (x)
  207.   (do ((args (reverse (cdr x)) (cdr args))
  208.        (result
  209.          nil
  210.          (cond ((atom (car args))
  211.                 (bq-attach-append *bq-append* (car args) result))
  212.                ((and (eq (caar args) *bq-list*)
  213.                      (notany #'bq-splicing-frob (cdar args)))
  214.                 (bq-attach-conses (cdar args) result))
  215.                ((and (eq (caar args) *bq-list**)
  216.                      (notany #'bq-splicing-frob (cdar args)))
  217.                 (bq-attach-conses
  218.                   (reverse (cdr (reverse (cdar args))))
  219.                   (bq-attach-append *bq-append*
  220.                                     (car (last (car args)))
  221.                                     result)))
  222.                ((and (eq (caar args) *bq-quote*)
  223.                      (consp (cadar args))
  224.                      (not (bq-frob (cadar args)))
  225.                      (null (cddar args)))
  226.                 (bq-attach-conses (list (list *bq-quote*
  227.                                               (caadar args)))
  228.                                   result))
  229.                ((eq (caar args) *bq-clobberable*)
  230.                 (bq-attach-append *bq-nconc* (cadar args) result))
  231.                (t (bq-attach-append *bq-append*
  232.                                     (car args)
  233.                                     result)))))
  234.       ((null args) result)))
  235.  
  236. (defun null-or-quoted (x)
  237.   (or (null x) (and (consp x) (eq (car x) *bq-quote*))))
  238.  
  239. ;;; When BQ-ATTACH-APPEND is called, the OP should be #:BQ-APPEND
  240. ;;; or #:BQ-NCONC.  This produces a form (op item result) but
  241. ;;; some simplifications are done on the fly:
  242. ;;;
  243. ;;;  (op '(a b c) '(d e f g)) => '(a b c d e f g)
  244. ;;;  (op item 'nil) => item, provided item is not a splicable frob
  245. ;;;  (op item 'nil) => (op item), if item is a splicable frob
  246. ;;;  (op item (op a b c)) => (op item a b c)
  247.  
  248. (defun bq-attach-append (op item result)
  249.   (cond ((and (null-or-quoted item) (null-or-quoted result))
  250.          (list *bq-quote* (append (cadr item) (cadr result))))
  251.         ((or (null result) (equal result *bq-quote-nil*))
  252.          (if (bq-splicing-frob item) (list op item) item))
  253.         ((and (consp result) (eq (car result) op))
  254.          (list* (car result) item (cdr result)))
  255.         (t (list op item result))))
  256.  
  257. ;;; The effect of BQ-ATTACH-CONSES is to produce a form as if by
  258. ;;; `(LIST* ,@items ,result) but some simplifications are done
  259. ;;; on the fly.
  260. ;;;
  261. ;;;  (LIST* 'a 'b 'c 'd) => '(a b c . d)
  262. ;;;  (LIST* a b c 'nil) => (LIST a b c)
  263. ;;;  (LIST* a b c (LIST* d e f g)) => (LIST* a b c d e f g)
  264. ;;;  (LIST* a b c (LIST d e f g)) => (LIST a b c d e f g)
  265.  
  266. (defun bq-attach-conses (items result)
  267.   (cond ((and (every #'null-or-quoted items)
  268.               (null-or-quoted result))
  269.          (list *bq-quote*
  270.                (append (mapcar #'cadr items) (cadr result))))
  271.         ((or (null result) (equal result *bq-quote-nil*))
  272.          (cons *bq-list* items))
  273.         ((and (consp result)
  274.               (or (eq (car result) *bq-list*)
  275.                   (eq (car result) *bq-list**)))
  276.          (cons (car result) (append items (cdr result))))
  277.         (t (cons *bq-list** (append items (list result))))))
  278.  
  279. ;;; Removes funny tokens and changes (#:BQ-LIST* a b) into
  280. ;;; (CONS a b) instead of (LIST* a b), purely for readability.
  281.  
  282. (defun bq-remove-tokens (x)
  283.   (cond ((eq x *bq-list*) 'list)
  284.         ((eq x *bq-append*) 'append)
  285.         ((eq x *bq-nconc*) 'nconc)
  286.         ((eq x *bq-list**) 'list*)
  287.         ((eq x *bq-quote*) 'quote)
  288.         ((atom x) x)
  289.         ((eq (car x) *bq-clobberable*)
  290.          (bq-remove-tokens (cadr x)))
  291.         ((and (eq (car x) *bq-list**)
  292.               (consp (cddr x))
  293.               (null (cdddr x)))
  294.          (cons 'cons (maptree #'bq-remove-tokens (cdr x))))
  295.         (t (maptree #'bq-remove-tokens x))))
  296.  
  297. Suppose that we first make the following definitions:
  298.  
  299. (setq q '(r s))
  300. (defun r (x) (reduce #'* x))
  301. (setq r '(3 5))
  302. (setq s '(4 6))
  303.  
  304. Without simplification, the notation $$(%%q) (which stands for ``(,,q)) is read
  305. as the expression
  306.  
  307. (APPEND (LIST 'APPEND) (LIST (APPEND (LIST 'LIST) (LIST Q))))
  308.  
  309. The value of this expression is
  310.  
  311. (APPEND (LIST (R S)))
  312.  
  313. and the value of this value is (24). We conclude that the net effect of
  314. twice-evaluating ``(,,q) is to take the value 24 of the value (r s) of q and
  315. plug it into the template ( ) to produce (24).
  316.  
  317. With simplification, the notation $$(%%q) is read as the expression
  318.  
  319. (LIST 'LIST Q)
  320.  
  321. The value of this expression is
  322.  
  323. (LIST (R S))
  324.  
  325. and the value of this value is (24). Thus the two ways of reading $$(%%q) do
  326. not produce the same expression-this we expected-but the values of the two ways
  327. are different as well. Only the values of the values are the same. In general,
  328. Common Lisp guarantees the result of an expression with backquotes nested to
  329. depth k only after k successive evaluations have been performed; the results
  330. after fewer than k evaluations are implementation-dependent.
  331.  
  332. (Note that in the expression `(foo ,(process `(bar ,x))) the backquotes are not
  333. doubly nested. The inner backquoted expression occurs within the textual scope
  334. of a comma belonging to the outer backquote. The correct way to determine the
  335. backquote nesting level of any subexpression is to start a count at zero and
  336. proceed up the S-expression tree, adding one for each backquote and subtracting
  337. one for each comma. This is similar to the rule for determining nesting level
  338. with respect to parentheses by scanning a character string linearly, adding or
  339. subtracting one as parentheses are passed.)
  340.  
  341. It is convenient to extend the ``=='' notation to handle multiple evaluation: x
  342. == == y means that the expressions x and y may have different results but they
  343. have the same results when twice evaluated. Similarly, x == ==== y means that
  344. the values of the values of the values of x and y are the same, and so on.
  345.  
  346. We can illustrate the differences between non-splicing and splicing backquote
  347. inclusions quite concisely:
  348.  
  349. $$(%%q)  ==
  350.   (APPEND (LIST 'APPEND) (LIST (APPEND (LIST 'LIST) (LIST Q))))
  351.   == == (LIST 'LIST Q) => (LIST (R S)) => (24)
  352.  
  353. $$(%@%q) ==
  354.   (APPEND (LIST 'APPEND) (LIST Q))
  355.   == == Q => (R S) => 24
  356.  
  357. $$(%%@q) ==
  358.   (APPEND (LIST 'APPEND) (LIST (APPEND (LIST 'LIST) Q)))
  359.   == == (CONS 'LIST Q) => (LIST R S) => ((3 5) (4 6))
  360.  
  361. $$(%@%@q) ==
  362.   (APPEND (LIST 'APPEND) Q)
  363.   == == (CONS 'APPEND Q) => (APPEND R S) => (3 5 4 6)
  364.  
  365. In each case I have shown both the unsimplified and simplified forms and then
  366. traced the intermediate evaluations of the simplified form. (Actually, the
  367. unsimplified forms do contain one simplification without which they would be
  368. unreadable: the nil that terminates each list has been systematically
  369. suppressed, so that one sees (append x y) rather than (append x y 'nil).)
  370.  
  371. The following driver function is useful for tracing the behavior of nested
  372. backquote syntax through multiple evaluations. The argument ls is a list of
  373. strings; each string will be processed by the reader (read-from-string). The
  374. argument n is the number of evaluations desired.
  375.  
  376. (defun try (ls &optional (n 0))
  377.   (dolist (x ls)
  378.     (format t "~&~A"
  379.             (substitute #¥` #¥$ (substitute #¥, #¥% x)))
  380.     (do ((form (macroexpand (read-from-string x)) (eval form))
  381.          (str " = " "~% => ")
  382.          (j 0 (+ j 1)))
  383.         ((>= j n)
  384.          (format t str)
  385.          (write form :pretty t))
  386.       (format t str)
  387.       (write form :pretty t)))
  388.   (format t "~¥&"))
  389.  
  390. This driver routine makes it easdy to explore a large number of cases
  391. systematically. Here is a list of examples that illustrate not only the
  392. differences between , and ,@ but also their interaction with '.
  393.  
  394. (setq fools2 '(
  395. "$$(foo %%p)"
  396. "$$(foo %%@q)"
  397. "$$(foo %'%r)"
  398. "$$(foo %'%@s)"
  399. "$$(foo %@%p)"
  400. "$$(foo %@%@q)"
  401. "$$(foo %@'%r)"
  402. "$$(foo %@'%@s)"
  403. ))
  404.  
  405. Consider this set of sample values:
  406.  
  407. (setq p '(union x y))
  408. (setq q '((union x y) (list 'sqrt 9)))
  409. (setq r '(union x y))
  410. (setq s '((union x y)))
  411.  
  412. Here is what happened when I executed (try fools2 2) with a non-nil value for
  413. the variable *bq-simplify* (to see simplified forms). I have interpolated some
  414. remarks.
  415.  
  416. ``(foo ,,p) = (LIST 'LIST ''FOO P)
  417.  => (LIST 'FOO (UNION X Y))
  418.  => (FOO (A B C))
  419.  
  420. So ,,p means ``the value of p is a form; use the value of the value of p.''
  421.  
  422. ``(foo ,,@q) = (LIST* 'LIST ''FOO Q)
  423.  => (LIST 'FOO (UNION X Y) (LIST 'SQRT 9))
  424.  => (FOO (A B C) (SQRT 9))
  425.  
  426. So ,,@q means ``the value of q is a list of forms; splice the list of values of
  427. the elements of the value of q.''
  428.  
  429. ``(foo ,',r) = (LIST 'LIST ''FOO (LIST 'QUOTE R))
  430.  => (LIST 'FOO '(UNION X Y))
  431.  => (FOO (UNION X Y))
  432.  
  433. So ,',r means ``the value of r may be any object; use the value of r that is
  434. available at the time of first evaluation, that is, when the outer backquote is
  435. evaluated.'' (To use the value of r that is available at the time of second
  436. evaluation, that is, when the inner backquote is evaluated, just use ,r.)
  437.  
  438. ``(foo ,',@s) = (LIST 'LIST ''FOO (CONS 'QUOTE S))
  439.  => (LIST 'FOO '(UNION X Y))
  440.  => (FOO (UNION X Y))
  441.  
  442. So ,',@s means ``the value of s must be a singleton list of any object; use the
  443. element of the value of s that is available at the time of first evaluation,
  444. that is, when the outer backquote is evaluated.'' Note that s must be a
  445. singleton list because it will be spliced into a form (quote ), and the quote
  446. special form requires exactly one subform to appear; this is generally true of
  447. the sequence ',@. (To use the value of s that is available at the time of
  448. second evaluation, that is, when the inner backquote is evaluated, just use
  449. ,@s,in which case the list s is not restricted to be singleton, or ,(car s).)
  450.  
  451. ``(foo ,@,p) = (LIST 'CONS ''FOO P)
  452.  => (CONS 'FOO (UNION X Y))
  453.  => (FOO A B C)
  454.  
  455. So ,@,p means ``the value of p is a form; splice in the value of the value of
  456. p.''
  457.  
  458. ``(foo ,@,@q) = (LIST 'CONS ''FOO (CONS 'APPEND Q))
  459.  => (CONS 'FOO (APPEND (UNION X Y) (LIST 'SQRT 9)))
  460.  => (FOO A B C SQRT 9)
  461.  
  462. So ,@,@q means ``the value of q is a list of forms; splice each of the values
  463. of the elements of the value of q, so that many splicings occur.''
  464.  
  465. ``(foo ,@',r) = (LIST 'CONS ''FOO (LIST 'QUOTE R))
  466.  => (CONS 'FOO '(UNION X Y))
  467.  => (FOO UNION X Y)
  468.  
  469. So ,@',r means ``the value of r must be a list; splice in the value of r that
  470. is available at the time of first evaluation, that is, when the outer backquote
  471. is evaluated.'' (To splice the value of r that is available at the time of
  472. second evaluation, that is, when the inner backquote is evaluated, just use
  473. ,@r.)
  474.  
  475. ``(foo ,@',@s) = (LIST 'CONS ''FOO (CONS 'QUOTE S))
  476.  => (CONS 'FOO '(UNION X Y))
  477.  => (FOO UNION X Y)
  478.  
  479. So ,@',@s means ``the value of s must be a singleton list whose element is a
  480. list; splice in the list that is the element of the value of s that is
  481. available at the time of first evaluation, that is, when the outer backquote is
  482. evaluated.'' (To splice the element of the value of s that is available at the
  483. time of second evaluation, that is, when the inner backquote is evaluated, just
  484. use ,@(car s).)
  485.  
  486. I leave it to the reader to explore the possibilities of triply nested
  487. backquotes.
  488.  
  489. (setq fools3 '(
  490. "$$$(foo %%%p)"     "$$$(foo %%%@q)"
  491. "$$$(foo %%'%r)"    "$$$(foo %%'%@s)"
  492. "$$$(foo %%@%p)"    "$$$(foo %%@%@q)"
  493. "$$$(foo %%@'%r)"   "$$$(foo %%@'%@s)"
  494. "$$$(foo %'%%p)"    "$$$(foo %'%%@q)"
  495. "$$$(foo %'%'%r)"   "$$$(foo %'%'%@s)"
  496. "$$$(foo %'%@%p)"   "$$$(foo %'%@%@q)"
  497. "$$$(foo %'%@'%r)"  "$$$(foo %'%@'%@s)"
  498. "$$$(foo %@%%p)"    "$$$(foo %@%%@q)"
  499. "$$$(foo %@%'%r)"   "$$$(foo %@%'%@s)"
  500. "$$$(foo %@%@%p)"   "$$$(foo %@%@%@q)"
  501. "$$$(foo %@%@'%r)"  "$$$(foo %@%@'%@s)"
  502. "$$$(foo %@'%%p)"   "$$$(foo %@'%%@q)"
  503. "$$$(foo %@'%'%r)"  "$$$(foo %@'%'%@s)"
  504. "$$$(foo %@'%@%p)"  "$$$(foo %@'%@%@q)"
  505. "$$$(foo %@'%@'%r)" "$$$(foo %@'%@'%@s)"
  506. ))
  507.  
  508. It is a pleasant exercise to construct values for p, q, r, and s that will
  509. allow execution of (try fools3 3) without error.
  510. [change_end]
  511.  
  512. -------------------------------------------------------------------------------
  513. [next]  [up] [previous]  [contents] [index]
  514. Next: References Up: Common Lisp the Language Previous: Discussion
  515. -------------------------------------------------------------------------------
  516.  
  517. -------------------------------------------------------------------------------
  518.  
  519. AI.Repository@cs.cmu.edu
  520.